package gov.nasa.anml.lifted;

import java.util.ArrayList;

import gov.nasa.anml.PDDL;
import gov.nasa.anml.utility.SimpleBoolean;
import gov.nasa.anml.utility.SimpleObject;

public class Lend<T extends SimpleObject<? super T>> extends BinaryExpression<T,T,T> {

	public Lend(Expression<T, T> l, Expression<T, ?> r) {
		super(l, r);
	}

	
	public PDDL.Time splitFirst(PDDL.Time t) {
		switch(t) {
		case All:
			return PDDL.Time.All;
		case StartHalf:
			return PDDL.Time.All;
		case EndHalf:
			return PDDL.Time.All;
		// if crammed into splitting atomic pieces up....
		case Interim:
			return PDDL.Time.All;
		case Start:
			// should be immediately before start: ...start)
			return PDDL.Time.Start;
		case End:
			// should be immediately before end: ...end)
			// instead of (start, end)
			return PDDL.Time.End;
		case Timeless:
		default:
			return t;
		}
	}
	
	public PDDL.Time splitRest(PDDL.Time t) {
		switch(t) {
		case All:
			return PDDL.Time.End;
		case StartHalf:
			return PDDL.Time.End;
		case EndHalf:
			return PDDL.Time.End;
		// if forced to split atomic things...
		case Interim:
			return PDDL.Time.End;
		case Start:
			return PDDL.Time.Interim;
		case End:
			return PDDL.Time.End;
		case Timeless:
		default:
			return t;
		}
	}
	
	public void translateStmt(PDDL pddl, Interval unit, PDDL.Time part) {
		ArrayList<PDDL.BooleanExpression> conditions = unit.getPDDLConditions(); 
		ArrayList<PDDL.Effect> effects = unit.getPDDLEffects();

		PDDL.Time pl,pr;
		switch(part) {
		case Start:
		case End:
			pl = pr = part;
			break;
		default:
			pl = PDDL.Time.Start;
			pr = PDDL.Time.End;
			break;
		}
		
		switch(left.typeCode()) {
		case Boolean:
			assert right.typeCode() == TypeCode.Boolean : "No operation combines booleans and other types directly";
			PDDL.PredicateReference refB = (PDDL.PredicateReference) left.translateLValue(pddl,unit);
			if (right instanceof SimpleBoolean) {
				boolean v = ((SimpleBoolean)right).v;
				if (v) {
					conditions.add(pddl.wrap(pl,pddl.makeTest(refB,false)));
					if (pl != pr)
						effects.add(pddl.wrap(pl,pddl.makeEffect(refB,true)));
					effects.add(pddl.wrap(pr,pddl.makeEffect(refB,false)));
				}
			} else {
				PDDL.BooleanExpression r = (PDDL.BooleanExpression) right.translateExpr(pddl,unit);
				conditions.add(pddl.wrap(pl,pddl.makeTest(PDDL.Op.implies,r,pddl.negate(refB))));
				if (pl != pr)
					effects.add(pddl.wrap(pl,pddl.makeEffect(r,refB,true)));
				effects.add(pddl.wrap(pr,pddl.makeEffect(r,refB,false)));
			}
			break;
		case Float:
			assert right.typeCode() == TypeCode.Float : "No operation combines floats and non-floats (at present).";
			PDDL.FunctionReference refF = (PDDL.FunctionReference) left.translateLValue(pddl,unit);
			PDDL.FloatExpression v = (PDDL.FloatExpression) right.translateExpr(pddl,unit);
			//conditions.add(pddl.wrap(pl,pddl.makeTest(PDDL.Op.gte,refF,v)));
			effects.add(pddl.wrap(pl,pddl.makeEffect(PDDL.Op.increase,refF,v)));
			effects.add(pddl.wrap(pr,pddl.makeEffect(PDDL.Op.decrease,refF,pddl.wrap(pl,v))));
			// the latter wrap is to make it very clear at what time we want the expression to be evaluated 
			// in order to calculate the amount of increase.
			// however, the resulting syntax is likely to break PDDL planners.
			break;
		default:
			System.err.println("Oops!");
			conditions.add(pddl.FalseCondition);
		}
	}
	
}
